package com.aca.market.service.impl;

import com.aca.market.entity.*;
import com.aca.market.mapper.*;
import com.aca.market.service.IIndentService;
import com.aca.market.service.IUserService;
import com.aca.market.util.UIDUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author aca
 * @since 2020-11-13
 */
@Transactional
@Service
@Slf4j
public class IndentServiceImpl extends ServiceImpl<IndentMapper, Indent> implements IIndentService {

    @Autowired
    IndentMapper indentMapper;
    @Autowired
    CartMapper cartMapper;
    @Autowired
    IUserService userService;
    @Autowired
    StorageMapper storageMapper;
    @Autowired
    IndentItemMapper indentItemMapper;
    @Autowired
    ItemMapper itemMapper;
    @Autowired
    CatalogMapper catalogMapper;


    @Override
    //获取月订单时间:收入的映射表
    public ConcurrentMap<String,Integer> getMonthlyIndents(){
        ConcurrentMap<String,Integer> monthIndents = new ConcurrentHashMap<>();
        List<Indent> indents = indentMapper.selectList(new QueryWrapper<Indent>()
                .select("indent_id", "payment", "create_time")
                .eq("status", 4)
                .between("create_time", LocalDateTime.now().minusMonths(1L), LocalDateTime.now()));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        for (Indent indent : indents) {
            String key = indent.getCreateTime().format(formatter);
            if (monthIndents.containsKey(key)){
                monthIndents.merge(key,indent.getPayment().intValue(),(oldVal,newVal)->oldVal+newVal);
            }else {
                monthIndents.put(key,indent.getPayment().intValue());
            }
        }
        return monthIndents;
    }

    @Override
    public Long paySteps(Indent indent){
        Indent init = initIndent(indent);
        log.info("INIT Indent ------------" + init);
        if (init != null){
            if (handleStorageAndCart()){
                Indent tmp = handlePayment(init);
                //若未处理 返回null 则是有错误的情况（过时间，价格被改
                if (tmp == null) return -1L;
                Indent res = simulate(tmp);
                if (res != null){
                    return res.getIndentId();
                }
            }
        }
        return -1L;
    }

    //订单初始化
    public Indent initIndent(Indent indent){

        Long userId = userService.nowUser().getId();

        Indent res = indent
                .setUserId(userId)
//                .setIndentId(UIDUtil.nextId())
                .setCreateTime(LocalDateTime.now())
                .setUpdateTime(LocalDateTime.now())
                .setCloseTime(LocalDateTime.now().plusMinutes(15L))
                .setStatus(0);
        boolean insertIndent = indentMapper.insert(res)>0;

        //还要处理订单商品表 先拿到购物车内信息 后插入
        List<Cart> cartList = cartMapper
                .selectList(new QueryWrapper<Cart>()
                        .eq("user_id", userId));
        boolean insertIndentItem = true;
        for (Cart cart : cartList) {
            Item item = itemMapper.selectById(cart.getItemId());
            IndentItem indentItem = new IndentItem()
                    .setUserId(userId)
                    .setItemId(cart.getItemId())
                    .setIndentId(indent.getIndentId())
                    .setItemName(item.getName())
                    .setCount(cart.getCount())
                    .setItemType(catalogMapper.selectById(item.getType()).getTypeId())
                    .setPriceSell(item.getPriceSell())
                    .setCreateTime(LocalDateTime.now())
                    .setUpdateTime(LocalDateTime.now());
            //判断操作成功与否
            insertIndentItem &= indentItemMapper.insert(indentItem)>0;
        }
        log.info("==================insertIndent" + insertIndent);
        log.info("==================insertIndentItem" + insertIndentItem);
        return (insertIndent&&insertIndentItem)?res:null;
    }

    // 结算购物车商品 减少对应商品的库存 购物车清空
    // 扣费 1.若在线支付 则扣余额 2.到付不扣费
    // 订单状态改变 1.1  余额不足 订单交易失败，用户可选择重来，超时就关闭
    // 1.2 余额够 扣余额(1) 交易成功(4) 虚拟发货(3)
    // 2 直接虚拟发货(3) -> 模拟线下支付现金->已付款(1)->交易成功(4)

    //返回处理结果 true成功
    private boolean handleStorageAndCart(){
        log.info("===========handleStorageAndCart");
        User user = userService.nowUser();
        //session有商品:数量的表
        Map<Item,Integer> map = (Map<Item, Integer>) userService.nowSession()
                .getAttribute("cart" + user.getId().toString());
        //库存操作 1 由上面的表查出库存 若购买数量>库存 则 购买失败 返回
        // 2若购买数量<=库存 则库存的数量减少对应数量
        //库存表=itemId：应该减少到的目标数量(<0时，即购买数量>库存)
        Map<Storage,Integer> storageMap = new TreeMap<>(Comparator.comparingLong(Storage::getItemId));
        //别拿错map
        for (Map.Entry<Item, Integer> entry : map.entrySet()) {
            //for update加行锁
            Storage storage = storageMapper.selectOne(new QueryWrapper<Storage>()
                    .eq("item_id",entry.getKey().getId())
                    .last("for update"));
            //库存-购买量
            int afterCutNum = storage.getInventory()-entry.getValue();
            //买的过多或超过限额
            if (afterCutNum < 0 || entry.getValue() > entry.getKey().getQuota()) {
                log.info("====================买的过多或超过限额");
                return false;
            }
            storageMap.put(storage,afterCutNum);
        }

//        log.info("=====>map: {}",map);
//        log.info("=====>storageMap：{}",storageMap);

        boolean updateFlag = true;
        for (Map.Entry<Storage, Integer> sEntry : storageMap.entrySet()) {
            Storage tmp = sEntry.getKey();
            //修改库存
            log.info("===============修改前库存："+ tmp.getInventory());
            //这是应该减少的数量
//            log.info("==========目标库存："+sEntry.getValue());
//            tmp.setInventory(tmp.getInventory() - sEntry.getValue());
            log.info("===============修改后库存："+ sEntry.getValue());

            updateFlag &= storageMapper.update(tmp,new UpdateWrapper<Storage>()
                    .set("inventory",sEntry.getValue())
                    .set("update_time",LocalDateTime.now())
                    .eq("item_id",tmp.getItemId())
                    //时间戳的乐观锁
                    .eq("update_time",tmp.getUpdateTime())) > 0;
        }
        //item表的关联库存
        //storageMap的迭代器
        Iterator<Integer> iterator = storageMap.values().iterator();
        for (Item item : map.keySet()) {
            if (iterator.hasNext()){
                updateFlag &= itemMapper.update(item, new UpdateWrapper<Item>()
                        .eq("id", item.getId())
                        .eq("updated_time",item.getUpdatedTime())
                        .set("inventory", iterator.next())
                        .set("updated_time",LocalDateTime.now())) > 0;
            }
        }

        boolean deleteFlag = cartMapper.delete(new QueryWrapper<Cart>()
                .eq("user_id",user.getId())) > 0;

        if (updateFlag && deleteFlag){
            log.info("=====库存更新完毕=======");
            log.info("=====购物车清空=======用户："+user.getId());
            return true;
        }
        return false;
    }

    //总价
//    private BigDecimal calSum(){
//        log.info("==============calSum");
//        User user = userService.nowUser();
//        Map<Item,Integer> map = (Map<Item, Integer>) userService.nowSession()
//                .getAttribute("cart" + user.getId().toString());
//        BigDecimal tol = BigDecimal.ZERO;
//        for (Map.Entry<Item, Integer> entry : map.entrySet()) {
//            tol.add(entry.getKey().getPriceSell().multiply(BigDecimal.valueOf(entry.getValue())));
//        }
//        return tol;
//    }


    //处理消费 返回改变状态的实体  0未付款 1已付款 2未发货 3已发货 4交易成功 5交易关闭 6交易失败
    private Indent handlePayment(Indent indent){
        log.info("==============handlePayment");
        //特判 过时间不处理
        if (indent.getCloseTime().isBefore(LocalDateTime.now())){
            log.info("===========订单超时已关闭,不处理");
            return null;
        }
        //计算总价 万一总价被前端篡改？ 并不会 前面cart从item拿就保证了
//        BigDecimal tol = calSum();
//        if (!tol.equals(indent.getPayment())){
//            log.info("calSum()==================>" + tol
//                    + ", indent.getPayment()===>"+indent.getPayment());
//            return null;
//        }
        Indent tmp = indent;
        tmp.setEndTime(LocalDateTime.now().plusMinutes(15L));
        //PaymentType: 1在线 2到付
        //总价

        if (tmp.getPaymentType().equals(2)){
            tmp.setStatus(2);
            tmp.setPaymentTime(LocalDateTime.now());
            return tmp;
        } else if (tmp.getPaymentType().equals(1)) {
            User user = userService.nowUser();
            //支付
            if (!payment(user, tmp)) return null;
            tmp.setStatus(1);
            return indentMapper.update(tmp, new UpdateWrapper<Indent>()
                    //时间戳
                    .eq("update_time", indent.getUpdateTime())
                    .eq("indent_id", tmp.getIndentId())
                    .set("update_time", tmp.getUpdateTime())
                    .set("payment_time", tmp.getPaymentTime())
                    .set("end_time", tmp.getEndTime())
                    .set("status", tmp.getStatus())) > 0 ? tmp : null;
        }
        tmp.setStatus(6);
        return indentMapper.update(tmp,new UpdateWrapper<Indent>()
                .eq("update_time",indent.getUpdateTime())
                .eq("indent_id",tmp.getIndentId())
                .set("update_time",tmp.getUpdateTime())
                .set("payment_time",tmp.getPaymentTime())
                .set("end_time",tmp.getEndTime())
                .set("status",tmp.getStatus()))>0?tmp:null;
    }

    private boolean payment(User user,Indent indent){
        log.info("===========payment");
        if (user.getBalance().compareTo(indent.getPayment()) >= 0) {
            //扣款
            log.info("**********已扣款：{} ,余额：{}",indent.getPayment(),user.getBalance().subtract(indent.getPayment()));
            return userService.update(new UpdateWrapper<User>()
                    .eq("id",user.getId())
                    .eq("updated_time",user.getUpdatedTime())
                    .set("balance",user.getBalance().subtract(indent.getPayment()))
                    .set("updated_time",LocalDateTime.now()));
        }
        log.info("===========余额不足");
        return false;
    }


//    @Async("asyncServiceExecutor")
    //统一模拟发货
    private Indent simulate(Indent indent){
        log.info("========================simulate");
        //特判 过时间不处理
        if (indent.getCloseTime().isBefore(LocalDateTime.now())){
            log.info("================= indent overtime");
            return null;
        }
        Indent tmp = indent;
        Integer status = tmp.getStatus();
        if (status.equals(2) || status.equals(1)){
            LocalDateTime now = LocalDateTime.now();
            //发货
            log.info("======================发货，订单状态更新");
            return indentMapper.update(tmp,new UpdateWrapper<Indent>()
                    .eq("indent_id",tmp.getIndentId())
                    //时间戳 不能这样用 这时候事务还没提交 这样找不到库内的updateTime
//                    .eq("update_time",tmp.getUpdateTime())
                    .set("consign_time",now)
                    //模拟 一分钟后完成交易
                    .set("update_time",now.plusMinutes(1L))
                    .set("end_time",now.plusMinutes(1L))
                    .set("status",4)
                    .set("close_time",now.plusMinutes(1L))
                    .set("shipping_name","默认快递")
                    .set("shipping_code",UIDUtil.getUUID().substring(0,6))) > 0 ?tmp:null;
        }
        //其他状态不处理 状态不对就返回null
        return null;
    }


}
